/* -*- C++ -*- */

 /****************************************************************************
 ** Copyright (c) quickfixengine.org  All rights reserved.
 **
 ** This file is part of the QuickFIX FIX Engine
 **
 ** This file may be distributed under the terms of the quickfixengine.org
 ** license as defined by quickfixengine.org and appearing in the file
 ** LICENSE included in the packaging of this file.
 **
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** See http://www.quickfixengine.org/LICENSE for licensing information.
 **
 ** Contact ask@quickfixengine.org if any conditions of this licensing are
 ** not clear to you.
 **
 ****************************************************************************/

 #ifndef FIX_DATADICTIONARY_H
 #define FIX_DATADICTIONARY_H

 #ifdef _MSC_VER
 #pragma warning( disable : 4503 4355 4786 4290 )
 #endif

 #include "Fields.h"
 #include "FieldMap.h"
 #include "DOMDocument.h"
 #include "Exceptions.h"
 #include <set>
 #include <map>
 #include <string.h>
 #include "FASTApi.h"

 namespace FIX
 {
 class FieldMap;
 class Message;
 class Session;

 /**
  * Represents a data dictionary for a version of %FIX.
  *
  * Generally loaded from an XML document.  The DataDictionary is also
  * responsible for validation beyond the basic structure of a message.
  */

 class DataDictionary
 {

   friend class Message;
   friend class Session;
   typedef std::set < int > MsgFields;
   typedef std::map < std::string, MsgFields > MsgTypeToField;
   typedef std::set < std::string > MsgTypes;
   typedef std::set < int > Fields;
   typedef std::map < int, bool > NonBodyFields;
   typedef std::vector< int > OrderedFields;
   typedef int* OrderedFieldsArray;
   typedef std::map < int, TYPE::Type > FieldTypes;
   typedef std::set < std::string > Values;
   typedef std::map < int, Values > FieldToValue;
   typedef std::map < int, std::string > FieldToName;
   typedef std::map < std::string, int > NameToField;
   typedef std::map < std::pair < int, std::string > ,
   std::string  > ValueToName;
   typedef std::map < std::pair < std::string, int > ,
   std::pair < int, DataDictionary* > > FieldToGroup;

   typedef std::map < std::string, FIX::Message > MessageFactory;
   typedef std::map < std::string, int > Msg2TemplateId;
   typedef std::map < int, std::string > TemplateId2Msg;
   typedef std::vector<FastTag> FastTagVector;
   typedef std::map < u32, FastTagVector > TID2OrderedFields;
   typedef std::map < u32, u32 > CheckOrderedFields;


 public:
   DataDictionary();
   DataDictionary( const DataDictionary& copy );
   DataDictionary( std::istream& stream ) throw( ConfigError );
   DataDictionary( const std::string& url ) throw( ConfigError );
   virtual ~DataDictionary();

   void readFromURL( const std::string& url ) throw( ConfigError );
   void readFromDocument( DOMDocumentPtr pDoc ) throw( ConfigError );
   void readFromStream( std::istream& stream ) throw( ConfigError );

   int AddXMLFASTField(DOMDocument* pDoc, DOMNode* pMessageFieldNode, bool &bMsgTypeFound,
       int tid, std::string &msgType, bool &bRequired, int &fieldSlot, DataDictionary &DD,
       int &ifield, const std::string & msgName) throw( ConfigError );

   void addXMLFASTGroup( DOMDocument* pDoc, DOMNode* pNode,
                                  const std::string& msgtype,
                                  DataDictionary& DD) throw( ConfigError );
    void readFromFASTDocument( DOMDocumentPtr pDoc )throw( ConfigError );

   int* getOrderedFields() const;

   // storage functions
   void setVersion( const std::string& beginString )
   {
     m_beginString = beginString;
     m_hasVersion = true;
   }
   std::string getVersion() const
   {
     return m_beginString;
   }

   void addField( int field )
   {
     m_fields.insert( field );
     m_orderedFields.push_back( field );
   }

   void addFASTField( int field )
   {
     if ( !isField(field) )
     {
        m_fields.insert( field );
        m_orderedFields.push_back( field );
     }
   }

  
   void addFieldName( int field, const std::string& name )
   {
     if( m_names.insert( std::make_pair(name, field) ).second == false )
       throw ConfigError( "Field named " + name + " defined multiple times" );
     m_fieldNames[field] = name;
   }
   void addFASTFieldName( int field, const std::string& name )
   {
     if( m_names.insert( std::make_pair(name, field) ).second == false )
       return;
     m_fieldNames[field] = name;
   }
   bool getFieldName( int field, std::string& name ) const
   {
     FieldToName::const_iterator i = m_fieldNames.find( field );
     if(i == m_fieldNames.end()) return false;
     name = i->second;
     return true;
   }

   bool getFieldTag( std::string name, int& field ) const
   {
     NameToField::const_iterator i = m_names.find( name );
     if(i == m_names.end()) return false;
     field = i->second;
     return true;
   }

   void addValueName( int field, const std::string& value, const std::string& name )
   {
     m_valueNames[std::make_pair(field, value)] = name;
   }

   bool getValueName( int field, const std::string& value, std::string& name ) const
   {
     ValueToName::const_iterator i = m_valueNames.find( std::make_pair(field, value) );
     if(i == m_valueNames.end()) return false;
     name = i->second;
     return true;
   }

   bool isField( int field ) const
   {
     return m_fields.find( field ) != m_fields.end();
   }

   void addMsgType( const std::string& msgType )
   {
     m_messages.insert( msgType );
   }

   void addTemplateID( const std::string& msgType, long tid )
   {
           m_Message2TemplateId.insert( std::make_pair(msgType, tid) );
           m_TemplateId2Message.insert( std::make_pair(tid, msgType) );
   }

   bool isMsgType( const std::string& msgType ) const
   {
     return m_messages.find( msgType ) != m_messages.end();
   }

   bool isTagType( unsigned int tid ) const
   {
     return m_SortedFieldValues.find( tid ) != m_SortedFieldValues.end();
   }

   void addMsgField( const std::string& msgType, int field )
   {
     m_messageFields[ msgType ].insert( field );

   }

   void addMsgFieldTag( FastTag ft )
   {

           if ( m_SortedFieldValues.find(ft.get_tid()) != m_SortedFieldValues.end())
           {
                   bool isPresent = true;
                   for ( FastTagVector::iterator start = m_SortedFieldValues[ft.get_tid()].begin(); start != m_SortedFieldValues[ft.get_tid()].end(); start++ )
                   {
                           if ( start->get_num() ==  ft.get_num() )
                           {
                                   isPresent = false;
                           }
                   }
                   if ( isPresent )
                   {
                           m_SortedFieldValues[ ft.get_tid() ].push_back( ft );
                   }
           }
           else
           {
                   m_SortedFieldValues[ ft.get_tid() ].push_back( ft );
           }
   }

   MsgFields getFields( const std::string& msgType) const
   {
           MsgTypeToField::const_iterator i = m_messageFields.find( msgType );
           MsgFields m = i->second;
           return m;
   }

   FastTagVector getFieldTag( u32 tid ) const
   {
           TID2OrderedFields::const_iterator i = m_SortedFieldValues.find( tid );
           FastTagVector o = i->second;
           return o;
   }
   FastTagVector getFieldTag( u32 tid )
   {
           TID2OrderedFields::iterator i = m_SortedFieldValues.find( tid );
           FastTagVector o = i->second;
           return o;
   }

   NonBodyFields getHeader( ) const
   {
           return m_headerFields;
   }

   NonBodyFields getTrailer( ) const
   {
           return m_trailerFields;
   }

   MsgFields getRequired( const std::string& msgType ) const
   {
           MsgTypeToField::const_iterator i = m_requiredFields.find( msgType );
           MsgFields m = i->second;
           return m;
   }


   bool isMsgField( const std::string& msgType, int field ) const
   {
     MsgTypeToField::const_iterator i = m_messageFields.find( msgType );
     if ( i == m_messageFields.end() ) return false;
     return i->second.find( field ) != i->second.end();
   }

   void addHeaderField( int field, bool required )
   {
     m_headerFields[ field ] = required;
   }
   void setHeaderField( int field, bool required )
   {
     m_headerFields[ field ] = required;
   }

   bool isHeaderField( int field ) const
   {
     return m_headerFields.find( field ) != m_headerFields.end();
   }

   void addTrailerField( int field, bool required )
   {
     m_trailerFields[ field ] = required;
   }
   void setTrailerOrderField( int field, bool required )
   {
     m_trailerFields[ field ] = required;
   }

   bool isTrailerField( int field ) const
   {
     return m_trailerFields.find( field ) != m_trailerFields.end();
   }

   void addFieldType( int field, TYPE::Type type )
   {
     m_fieldTypes[ field ] = type;
   }
   void addFASTFieldType( int field, TYPE::Type type )
   {
     FieldTypes::const_iterator i = m_fieldTypes.find( field );
     if ( i != m_fieldTypes.end() )
         return;
     m_fieldTypes[ field ] = type;
   }

   bool getFieldType( int field, TYPE::Type& type ) const
   {
     FieldTypes::const_iterator i = m_fieldTypes.find( field );
     if ( i == m_fieldTypes.end() ) return false;
     type = i->second;
     return true;
   }

   bool getFieldType( int field, eFAST_TYPE& FastType ) const
   {
     FieldTypes::const_iterator i = m_fieldTypes.find( field );
     if ( i == m_fieldTypes.end() ) return false;
     TYPE::Type type = i->second;
         FastType = TypeToFastType(type);
     return true;
   }
   bool getFieldOpearator( std::string op, eFAST_OP& FastOperator ) const
   {
           FastOperator = eFAST_OP_NONE;

           if ( op == "none" ) FastOperator = eFAST_OP_NONE;
           if ( op == "default" ) FastOperator = eFAST_OP_DEFAULT;
           if ( op == "constant" ) FastOperator = eFAST_OP_CONST;
           if ( op == "copy" ) FastOperator = eFAST_OP_COPY;
           if ( op == "increment" ) FastOperator = eFAST_OP_INCR;
           if ( op == "delta" ) FastOperator = eFAST_OP_DELTA;
           if ( op == "tail" ) FastOperator = eFAST_OP_TAIL;
           return true;
   }


   void addRequiredField( const std::string& msgType, int field )
 {
     m_requiredFields[ msgType ].insert( field );
   }

   bool isRequiredField( const std::string& msgType, int field ) const
   {
     MsgTypeToField::const_iterator i = m_requiredFields.find( msgType );
     if ( i == m_requiredFields.end() ) return false;
     return i->second.find( field ) != i->second.end();
   }

   bool isRequired( const std::string& msgType ) const
   {
     MsgTypeToField::const_iterator i = m_requiredFields.find( msgType );
     if ( i == m_requiredFields.end() ) return false;
         return true;
   }

   void addFieldValue( int field, const std::string& value )
   {
     m_fieldValues[ field ].insert( value );
   }

   bool hasFieldValue( int field ) const
   {
     FieldToValue::const_iterator i = m_fieldValues.find( field );
     return i != m_fieldValues.end();
   }

   bool isFieldValue( int field, const std::string& value ) const
   {
     FieldToValue::const_iterator i = m_fieldValues.find( field );
     if ( i == m_fieldValues.end() )
       return false;
     if( !isMultipleValueStringField( field ) )
       return i->second.find( value ) != i->second.end();

     // MultipleValueString
     std::string::size_type startPos = 0;
     std::string::size_type endPos = 0;
     do
     {
       endPos = value.find_first_of(' ', startPos);
       std::string singleValue =
         value.substr( startPos, endPos - startPos );
       if( i->second.find( singleValue ) == i->second.end() )
         return false;
       startPos = endPos + 1;
     } while( endPos != std::string::npos );
     return true;
   }

   void addGroup( const std::string& msg, int field, int delim,
                  const DataDictionary& dataDictionary )
   {
     DataDictionary * pDD = new DataDictionary;
     *pDD = dataDictionary;
     pDD->setVersion( getVersion() );
     m_groups[ std::make_pair( msg, field ) ] = std::make_pair( delim, pDD );
   }

   bool isGroup( const std::string& msg, int field ) const
   {
     return m_groups.find( std::make_pair( msg, field ) ) != m_groups.end();
   }

   bool getGroup( const std::string& msg, int field, int& delim,
                  const DataDictionary*& pDataDictionary ) const
   {
     FieldToGroup::const_iterator i =
       m_groups.find( std::make_pair( msg, field ) );
     if ( i == m_groups.end() ) return false;
     std::pair < int, DataDictionary* > pair = i->second;
     delim = pair.first;
     pDataDictionary = pair.second;
     return true;
   }

   bool isDataField( int field ) const
   {
     FieldTypes::const_iterator i = m_fieldTypes.find( field );
     return i != m_fieldTypes.end() && i->second == TYPE::Data;
   }

   bool isMultipleValueStringField( int field ) const
   {
     FieldTypes::const_iterator i = m_fieldTypes.find( field );
     return i != m_fieldTypes.end() && i->second == TYPE::MultipleValueString;
   }

   void checkFieldsOutOfOrder( bool value )
   { m_checkFieldsOutOfOrder = value; }
   void checkFieldsHaveValues( bool value )
   { m_checkFieldsHaveValues = value; }
   void checkUserDefinedFields( bool value )
   { m_checkUserDefinedFields = value; }

   /// Validate a message.
   void validate( const Message& message ) throw( std::exception );
   DataDictionary& operator=( const DataDictionary& rhs );

 private:
   /// Iterate through fields while applying checks.
   void iterate( const FieldMap& map, const MsgType& msgType );

   /// Check if message type is defined in spec.
   void checkMsgType( const MsgType& msgType ) const
   {
     if ( !isMsgType( msgType.getValue() ) )
       throw InvalidMessageType();
   }

   /// If we need to check for the tag in the dictionary
   bool shouldCheckTag( const FieldBase& field ) const
   {
     if( !m_checkUserDefinedFields && field.getField() >= FIELD::UserMin )
       return false;
     else
       return true;
   }

   /// Check if field tag number is defined in spec.
   void checkValidTagNumber( const FieldBase& field ) const
   throw( InvalidTagNumber )
   {
     if( m_fields.find( field.getField() ) == m_fields.end() )
       throw InvalidTagNumber( field.getField() );
   }

   void checkValidFormat( const FieldBase& field ) const
   throw( IncorrectDataFormat )
   {
     try
     {
       TYPE::Type type = TYPE::Unknown;
       getFieldType( field.getField(), type );
       switch ( type )
       {
       case TYPE::String:
         STRING_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Char:
         CHAR_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Price:
         PRICE_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Int:
       case TYPE::UInt:
       case TYPE::Long:
       case TYPE::ULong:
         INT_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Amt:
         AMT_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Qty:
         QTY_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Currency:
         CURRENCY_CONVERTOR::convert( field.getString() ); break;
       case TYPE::MultipleValueString:
         MULTIPLEVALUESTRING_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Exchange:
         EXCHANGE_CONVERTOR::convert( field.getString() ); break;
       case TYPE::UtcTimeStamp:
         UTCTIMESTAMP_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Boolean:
         BOOLEAN_CONVERTOR::convert( field.getString() ); break;
       case TYPE::LocalMktDate:
         LOCALMKTDATE_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Data:
         DATA_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Float:
         FLOAT_CONVERTOR::convert( field.getString() ); break;
       case TYPE::PriceOffset:
         PRICEOFFSET_CONVERTOR::convert( field.getString() ); break;
       case TYPE::MonthYear:
         MONTHYEAR_CONVERTOR::convert( field.getString() ); break;
       case TYPE::DayOfMonth:
         DAYOFMONTH_CONVERTOR::convert( field.getString() ); break;
       case TYPE::UtcDate:
         UTCDATE_CONVERTOR::convert( field.getString() ); break;
       case TYPE::UtcTimeOnly:
         UTCTIMEONLY_CONVERTOR::convert( field.getString() ); break;
       case TYPE::NumInGroup:
         NUMINGROUP_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Percentage:
         PERCENTAGE_CONVERTOR::convert( field.getString() ); break;
       case TYPE::SeqNum:
       case TYPE::LongSeqNum:
         SEQNUM_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Length:
         LENGTH_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Country:
         COUNTRY_CONVERTOR::convert( field.getString() ); break;
       case TYPE::Unknown: break;
       }
     }
     catch ( FieldConvertError& )
     { throw IncorrectDataFormat( field.getField(), field.getString() ); }
   }

   void checkValue( const FieldBase& field ) const
   throw( IncorrectTagValue )
   {
     if ( !hasFieldValue( field.getField() ) ) return ;

     const std::string& value = field.getString();
     if ( !isFieldValue( field.getField(), value ) )
       throw IncorrectTagValue( field.getField() );
   }

   /// Check if a field has a value.
   void checkHasValue( const FieldBase& field ) const
   throw( NoTagValue )
   {
     if ( m_checkFieldsHaveValues && !field.getString().length() )
       throw NoTagValue( field.getField() );
   }

   /// Check if a field is in this message type.
   void checkIsInMessage
   ( const FieldBase& field, const MsgType& msgType ) const
   throw( TagNotDefinedForMessage )
   {
     if ( !isMsgField( msgType, field.getField() ) )
       throw TagNotDefinedForMessage( field.getField() );
   }

   /// Check if group count matches number of groups in
   void checkGroupCount
   ( const FieldBase& field, const FieldMap& fieldMap, const MsgType& msgType ) const
   throw( RepeatingGroupCountMismatch )
   {
     int fieldNum = field.getField();
     if( isGroup(msgType, fieldNum) )
     {
       if( fieldMap.groupCount(fieldNum)
         != IntConvertor::convert(field.getString()) )
       throw RepeatingGroupCountMismatch(fieldNum);
     }
   }

   /// Check if a message has all required fields.
   void checkHasRequired
   ( const FieldMap& header, const FieldMap& body, const FieldMap& trailer,
     const MsgType& msgType ) const
   throw( RequiredTagMissing )
   {
     NonBodyFields::const_iterator iNBF;
     for( iNBF = m_headerFields.begin(); iNBF != m_headerFields.end(); ++iNBF )
     {
       if( iNBF->second == true && !header.isSetField(iNBF->first) )
         throw RequiredTagMissing( iNBF->first );
     }

     for( iNBF = m_trailerFields.begin(); iNBF != m_trailerFields.end(); ++iNBF )
     {
       if( iNBF->second == true && !trailer.isSetField(iNBF->first) )
         throw RequiredTagMissing( iNBF->first );
     }

     MsgTypeToField::const_iterator iM
       = m_requiredFields.find( msgType.getString() );
     if ( iM == m_requiredFields.end() ) return ;

     const MsgFields& fields = iM->second;
     MsgFields::const_iterator iF;
     for( iF = fields.begin(); iF != fields.end(); ++iF )
     {
       if( !body.isSetField(*iF) )
         throw RequiredTagMissing( *iF );
     }

     FieldMap::g_iterator groups;
     for( groups = body.g_begin(); groups != body.g_end(); ++groups )
     {
       int delim;
       const DataDictionary* DD = 0;
       int field = groups->first;
       if( getGroup( msgType.getValue(), field, delim, DD ) )
       {
         std::vector<FieldMap*>::const_iterator group;
         for( group = groups->second.begin(); group != groups->second.end(); ++group )
           DD->checkHasRequired( **group, **group, **group, msgType );
       }
     }
   }

   /// Read XML file using MSXML.
   void readMSXMLDOM( const std::string& );
   void readMSXML( const std::string& );
   /// Read XML file using libXML.
   void readLibXml( const std::string& );

   int lookupXMLFieldNumber( DOMDocument*, DOMNode* ) const;
   int lookupXMLFieldNumber( DOMDocument*, const std::string& name ) const;
   int addXMLComponentFields( DOMDocument*, DOMNode*, const std::string& msgtype, DataDictionary&, bool );
   void addXMLGroup( DOMDocument*, DOMNode*, const std::string& msgtype, DataDictionary&, bool, int tid = 0 );
   TYPE::Type XMLTypeToType( const std::string& xmlType ) const;
   eFAST_TYPE TypeToFastType( const TYPE::Type& type ) const;
   TYPE::Type XMLFASTTypeToType( const std::string& type ) const;


   bool m_hasVersion;
   bool m_checkFieldsOutOfOrder;
   bool m_checkFieldsHaveValues;
   bool m_checkUserDefinedFields;
   BeginString m_beginString;
   MsgTypeToField m_messageFields;
   MsgTypeToField m_requiredFields;
   MsgTypes m_messages;
   Fields m_fields;
   OrderedFields m_orderedFields;
   mutable OrderedFieldsArray m_orderedFieldsArray;
   NonBodyFields m_headerFields;
   NonBodyFields m_trailerFields;
   FieldTypes m_fieldTypes;
   FieldToValue m_fieldValues;
   FieldToName m_fieldNames;
   NameToField m_names;
   ValueToName m_valueNames;
   FieldToGroup m_groups;
   TemplateId2Msg m_TemplateId2Message;
   Msg2TemplateId m_Message2TemplateId;
   TID2OrderedFields m_SortedFieldValues;
   CheckOrderedFields m_CheckOrderedFields;
  
 };
 }
  
 #endif //FIX_DATADICTIONARY_H
